﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using GeneralDFS.BusinessLogical.Config;
using GeneralDFS.Common;
using GeneralDFS.Common.Network;
using GeneralDFS.DataEntity.IO;
using GeneralDFS.DataEntity.Config;

namespace GeneralDFS.BusinessLogical.Network
{
    public class AsyncDataNodeTcpClient
    {
        private readonly TcpClient _client;
        private DFSConfig config;

        private long writeLength;       //表示已经发送的长度

        public AsyncDataNodeTcpClient(DFSConfig config,String ip, int port)
        {
            this.config = config;
            _client = new TcpClient();
            _client.Connect(new IPEndPoint(IPAddress.Parse(ip), port));
        }

        /// <summary>
        /// 异步写入
        /// </summary>
        /// <param name="metaData"></param>
        /// <param name="fileStream"></param>
        public void Write(FileBlockMetaData metaData, FileStream fileStream)
        {
            var state = new CustomTcpState
                {
                    NetworkStream = _client.GetStream(),
                    FileStream = fileStream,
                    Buffer = new Byte[config.DetailConfig.BuffSize],
                    PreBuffer = new byte[config.DetailConfig.BuffSize]
                };

            state.PreBufferSize = state.PreBuffer.Length;
            state.BufferSize = state.Buffer.Length;

            Byte[] metaDataBytes = SerializeHelper.GetBytes(metaData);
            Byte[] metaDataLengthBytes = BitConverter.GetBytes(metaDataBytes.Length);
            state.NetworkStream.Write(metaDataLengthBytes, 0, metaDataLengthBytes.Length);
            state.NetworkStream.Write(metaDataBytes, 0, metaDataBytes.Length);
            state.AllDone=new ManualResetEvent(false);
            state.PreEvent=new ManualResetEvent(false);
            fileStream.Seek(0, SeekOrigin.Begin);
            if (state.NetworkStream.CanWrite)
            {
                int length = fileStream.Read(state.Buffer, 0, state.BufferSize);
                if (length < state.BufferSize)
                    state.BufferSize = length;
                if (fileStream.Position < fileStream.Length)
                    state.FileStream.BeginRead(state.PreBuffer, 0, state.PreBufferSize, PreReadCallBack, state);
                writeLength = state.BufferSize;
                state.NetworkStream.BeginWrite(state.Buffer, 0, state.BufferSize, WriteCallBack, state);
                state.AllDone.WaitOne();
            }
        }

        private void PreReadCallBack(IAsyncResult result)
        {
            var state = result.AsyncState as CustomTcpState;
            state.PreBufferSize = state.FileStream.EndRead(result);
            state.PreEvent.Set();
        }

        /// <summary>
        /// 异步写入的回调函数
        /// </summary>
        /// <param name="result"></param>
        public void WriteCallBack(IAsyncResult result)
        {
            var state = result.AsyncState as CustomTcpState;
            state.AllDone.Set();
            var fileStream = state.FileStream;
            var networkStream = state.NetworkStream;
            if(null==networkStream||null==fileStream) return;
            networkStream.EndWrite(result);
            //如果还有需要预读的数据，等待预读
            if (writeLength < fileStream.Length)
            {
                state.PreEvent.WaitOne();
                state.PreEvent.Reset();
            }

            if (fileStream.CanRead&&fileStream.Position < fileStream.Length && networkStream.CanWrite)
            {
                Array.Copy(state.PreBuffer,0,state.Buffer,0,state.PreBufferSize);
                state.BufferSize = state.PreBufferSize;
                state.FileStream.BeginRead(state.PreBuffer, 0, state.PreBufferSize, PreReadCallBack, state);
                writeLength += state.BufferSize;
                networkStream.BeginWrite(state.Buffer, 0, state.BufferSize, WriteCallBack, state);
                state.AllDone.WaitOne();
            }
            else
            {
                if (writeLength < state.FileStream.Length)
                {
                    writeLength = state.FileStream.Length;
                    networkStream.BeginWrite(state.PreBuffer, 0, state.PreBufferSize, WriteCallBack, state);
                }
                else
                {
                    fileStream.Close();
                    state.NetworkStream.Close();
                }

            }
        }
    }
}
